Петя недавно узнал о понятии множества и вложенности
множеств. Он заинтересовался последовательностями S0 Ê S1 Ê S2 Ê … Ê Sn, у которых каждый следующий элемент – подмножество
предыдущего, S0 – некоторое конечное множество {a1, a2,
…, am}.
Петя отметил:
каждому из вложенных множеств можно поставить в соответствие целое число H(S)
= . Таким образом H(S0) = 2m – 1, H(Ø) = 0.
Петя
произвольным образом выбрал n чисел: h1, h2, …, hn.
Ему стало интересно: сколько существует разных наборов вложенных множеств S1,
S2, …, Sn при H(S1) = h1 (mod 41), H(S2) = h2 (mod 41), …, H(Sn)
= hn (mod 41)? К
сожалению, Петя сбился со счета, поэтому просит Вас о помощи.
Вход. Первая строка содержит два целых числа n и m
(1 ≤ n ≤ 5, 1 ≤ m ≤ 20). Вторая строка содержит n целых чисел h1, h2,
…, hn, каждое из которых
удовлетворяет условию: 0 ≤ hk
≤ 40.
Выход. Вывести искомое количество вложенных множеств.
Известно, что это число не превосходит 1018.
Пример
входа 1 3 5 7 3 3 |
Пример
входа 2 3 5 7 4 5 |
Пример
входа 3 1 7 3 |
Пример
входа 4 3 10 31 29 17 |
Пример выхода 1 1 |
Пример выхода 2 0 |
Пример выхода 3 4 |
Пример выхода 4 28 |
динамическое
программирование - маски подмножеств
Анализ алгоритма
Пусть F(n – i,
Si) равно количеству
вложенных множеств Si Ê Si+1
Ê … Ê Sn, для которых H(Si+1) (mod 41) = hi+1, H(Si+2) (mod 41) = hi+2, …, H(Sn) (mod 41) = hn.
Например, F(1, Sn-1) равно
количеству вложенных множеств Sn-1
Ê Sn,
для которых H(Sn) (mod 41)
= hn. Положим F(0, Sn) = 1 (имеется одно
множество Sn без
дополнительных ограничений на него).
Тогда
количеством вложенных множеств S0 Ê S1 Ê S2 Ê … Ê Sn будет значение F(n,
S0) = F(n, 2m – 1). Здесь 2m – 1 является маской
множества S0 = {a1,
a2, …, am}.
Задача решается
генерацией всех возможных искомых последовательностей. Для вычисления F(n, S0) перебираем все подмножества
S1 множества S0. Для каждого подмножества, для которого H(S1) % 41 = h1, далее решаем
подзадачу F(n – 1, S1),
которая ищет количество вложенных n
множеств, наибольшее из которых равно S1. Отсюда
F(n, S0) =
Хранение
значений функции F в ячейках массива f[n][2m] не пройдет по памяти при
заданных ограничениях на n и m. Значения F(n – i, Si) будут ненулевыми только
для таких подмножеств Si,
для которых H(Si) (mod 41)
= hi. Поэтому F(n – i,
H(Si)) можно хранить в f[n – i][
H(Si) / 41]. И тогда нам
достаточно будет массива размером f[n][2m / 41].
В тестах могут
встречаться значения hi =
0, на их обработку следует обратить внимание.
Пример
1. Существует
только один вариант: S1 = {a1,
a2, a3}, S2 = S3 = {a1, a2}. H(S1) = 7, H(S2) = H(S3)
= 3.
2. Такой
комбинации вложенных множеств не существует.
3. H({a1, a2}) = 21 – 1 + 22 – 1 = 3, H({a3, a4, a6})
= 22 + 23 + 25 = 4 + 8 + 32 = 44 = 41 + 3, Н({a1, a3, a5,
a7}) = 20 + 22
+ 24 + 26 = 1 + 4 + 16 + 64 = 85 = 2 · 41 + 3, Н({a2, a3, a4,
a5, a6, a7})
= 21 + 22 + 23 + 24 + 25
+ 26 = 2 + 4 + 8 + 16 + 32 + 64 = 126 = 3 · 41 + 3.
4. Представлено
на рисунке.
Реализация
алгоритма
Объявим
используемые массивы.
int h[5];
long long
f[6][(1 << 20) / 41 + 10];
Вычислим значение F(n,
mask).
long long
F(int n, int
mask)
{
if (n == 0) return 1;
int m = mask
/ 41;
if (f[n][m]
!= -1) return f[n][m];
f[n][m] = 0;
Перебираем все подмаски sub_mask маски mask. Поскольку возможны hi = 0, то следует
обрабатывать и нулевые подмаски.
for (int sub_mask = mask; ; sub_mask = (sub_mask - 1)
& mask)
{
if
(sub_mask % 41 == h[N - n])
f[n][m] += F(n - 1, sub_mask);
if
(sub_mask == 0) break;
}
return
f[n][m];
}
Основная часть программы.Читаем
входные данные.
scanf("%d %d", &N, &M);
for (i = 0; i < N; i++)
scanf("%d",
&h[i]);
memset(f, -1, sizeof(f));
Выводим ответ задачи F(n,
2m – 1).
printf("%lld\n", F(N, (1 << M) - 1));